home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 30
/
Aminet 30 (1999)(Schatztruhe)[!][Apr 1999].iso
/
Aminet
/
dev
/
cross
/
GBDK-2.0.lha
/
GBDK
/
lib
/
crt0.s
< prev
next >
Wrap
Text File
|
1998-10-30
|
8KB
|
540 lines
.include "global.s"
;; ****************************************
;; Beginning of module
.title "GB Runtime"
.module Runtime
.area _HEADER (ABS)
;; Standard header for the GB
.org 0x00
RET ; Empty function (default for interrupts)
.org 0x10
.byte 0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01
.byte 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80
;; Interrupt vectors
.org 0x40 ; VBL
.int_VBL:
PUSH HL
LD HL,#.int_0x40
JP .int
.org 0x48 ; LCD
.int_LCD:
PUSH HL
LD HL,#.int_0x48
JP .int
.org 0x50 ; TIM
.int_TIM:
PUSH HL
LD HL,#.int_0x50
JP .int
.org 0x58 ; SIO
.int_SIO:
PUSH HL
LD HL,#.int_0x58
JP .int
.org 0x60 ; JOY
.int_JOY:
PUSH HL
LD HL,#.int_0x60
JP .int
.int:
PUSH AF
PUSH BC
PUSH DE
1$:
LD A,(HL+)
OR (HL)
JR Z,2$
PUSH HL
LD A,(HL-)
LD L,(HL)
LD H,A
CALL 3$
POP HL
INC HL
JR 1$
2$:
POP DE
POP BC
POP AF
POP HL
RETI
3$:
JP (HL)
;; GameBoy Header
;; DO NOT CHANGE...
.org 0x100
.header:
NOP
JP 0x150
.byte 0xCE,0xED,0x66,0x66
.byte 0xCC,0x0D,0x00,0x0B
.byte 0x03,0x73,0x00,0x83
.byte 0x00,0x0C,0x00,0x0D
.byte 0x00,0x08,0x11,0x1F
.byte 0x88,0x89,0x00,0x0E
.byte 0xDC,0xCC,0x6E,0xE6
.byte 0xDD,0xDD,0xD9,0x99
.byte 0xBB,0xBB,0x67,0x63
.byte 0x6E,0x0E,0xEC,0xCC
.byte 0xDD,0xDC,0x99,0x9F
.byte 0xBB,0xB9,0x33,0x3E
;; Title of the game
.org 0x134
.asciz "Title"
.org 0x144
.byte 0,0,0
;; Cartridge type is ROM only
.org 0x147
.byte 0
;; ROM size is 32kB
.org 0x148
.byte 0
;; RAM size is 0kB
.org 0x149
.byte 0
;; Maker ID
.org 0x14A
.byte 0x00,0x00
;; Version number
.org 0x14C
.byte 0x01
;; Complement check
.org 0x14D
.byte 0x00
;; Checksum
.org 0x14E
.byte 0x00,0x00
;; ****************************************
.org 0x150
.code_start:
;; Beginning of the code
DI ; Disable interrupts
LD D,A ; Store CPU type in D
XOR A
;; Initialize the stack
LD SP,#.STACK
;; Clear from 0xC000 to 0xDFFF
LD HL,#0xDFFF
LD C,#0x20
LD B,#0x00
1$:
LD (HL-),A
DEC B
JR NZ,1$
DEC C
JR NZ,1$
;; Clear from 0xFE00 to 0xFEFF
LD HL,#0xFEFF
LD B,#0x00
2$:
LD (HL-),A
DEC B
JR NZ,2$
;; Clear from 0xFF80 to 0xFFFF
LD HL,#0xFFFF
LD B,#0x80
3$:
LD (HL-),A
DEC B
JR NZ,3$
; LD (.mode),A ; Clearing (.mode) is performed when clearing RAM
;; Store CPU type
LD A,D
LD (__cpu),A
;; Turn the screen off
CALL .display_off
;; Initialize the display
XOR A
LDH (.SCY),A
LDH (.SCX),A
LDH (.STAT),A
LDH (.WY),A
LD A,#0x07
LDH (.WX),A
;; Copy refresh_OAM routine to HIRAM
LD BC,#.refresh_OAM
LD HL,#.start_refresh_OAM
LD B,#.end_refresh_OAM-.start_refresh_OAM
4$:
LD A,(HL+)
LDH (C),A
INC C
DEC B
JR NZ,4$
;; Install interrupt routines
LD BC,#.vbl
CALL .add_VBL
LD BC,#.serial_IO
CALL .add_SIO
;; Standard color palettes
LD A,#0b11100100 ; Grey 3 = 11 (Black)
; Grey 2 = 10 (Dark grey)
; Grey 1 = 01 (Light grey)
; Grey 0 = 00 (Transparent)
LDH (.BGP),A
LDH (.OBP0),A
LD A,#0b00011011
LDH (.OBP1),A
;; Turn the screen on
LD A,#0b11000000 ; LCD = On
; WindowBank = 0x9C00
; Window = Off
; BG Chr = 0x8800
; BG Bank = 0x9800
; OBJ = 8x8
; OBJ = Off
; BG = Off
LDH (.LCDC),A
XOR A
LDH (.IF),A
LD A,#0b00001001 ; Pin P10-P13 = Off
; Serial I/O = On
; Timer Ovfl = Off
; LCDC = Off
; V-Blank = On
LDH (.IE),A
XOR A
LDH (.NR52),A ; Turn sound off
LDH (.SC),A ; Use external clock
LD A,#.DT_IDLE
LDH (.SB),A ; Send IDLE byte
LD A,#0x80
LDH (.SC),A ; Use external clock
CALL .init
EI ; Enable interrupts
;; Call the main function
CALL _main
99$:
JR 99$ ; Wait forever
.org .MODE_TABLE
;; Jump table for modes
RET
;; ****************************************
;; Ordering of segments for the linker
.area _CODE
.area _DATA
.area _LIT
.area _BSS
.area _HEAP ; HEAP is for malloc
.area _BSS
__cpu::
.ds 0x01 ; GB type (GB, PGB, CGB)
.mode::
.ds 0x01 ; Current mode
__io_out::
.ds 0x01 ; Byte to send
__io_in::
.ds 0x01 ; Received byte
__io_status::
.ds 0x01 ; Status of serial IO
.vbl_done::
.ds 0x01 ; Is VBL interrupt finished?
.int_0x40::
.blkw 0x08
.int_0x48::
.blkw 0x08
.int_0x50::
.blkw 0x08
.int_0x58::
.blkw 0x08
.int_0x60::
.blkw 0x08
;; Runtime library
.area _CODE
;; Call the initialization function for the mode specified in HL
.set_mode::
LD A,L
LD (.mode),A
LD BC,#.MODE_TABLE
SLA L ; Multiply mode by 4
SLA L
ADD HL,BC
JP (HL) ; Jump to initialization routine
;; Add interrupt routine in BC to the interrupt list
.add_VBL::
LD HL,#.int_0x40
JP .add_int
.add_LCD::
LD HL,#.int_0x48
JP .add_int
.add_TIM::
LD HL,#.int_0x50
JP .add_int
.add_SIO::
LD HL,#.int_0x58
JP .add_int
.add_JOY::
LD HL,#.int_0x60
JP .add_int
;; Remove interrupt BC from interrupt list HL if it exists
;; Abort if a 0000 is found (end of list)
;; Will only remove last int on list
.remove_int::
1$:
LD A,(HL+)
LD E,A
LD D,(HL)
OR D
RET Z ; No interrupt found
LD A,E
CP C
JR NZ,1$
LD A,D
CP B
JR NZ,1$
XOR A
LD (HL-),A
LD (HL),A
INC A ; Clear Z flag
RET
;; Add interrupt routine in BC to the interrupt list in HL
.add_int:
1$:
LD A,(HL+)
OR (HL)
JR Z,2$
INC HL
JR 1$
2$:
LD (HL),B
DEC HL
LD (HL),C
RET
;; VBlank interrupt
.vbl:
CALL .refresh_OAM
.if 0
;; Verify that only one VBlank interrupt occured
LD A,(.vbl_done)
OR A
JR Z,1$
LDH A,(.BGP)
CPL
LDH (.BGP),A
1$:
.endif
LD A,#0x01
LD (.vbl_done),A
RET
;; Wait for VBL interrupt to be finished
.wait_vbl_done::
_wait_vbl_done::
;; Check if the screen is on
LDH A,(.LCDC)
ADD A
RET NC ; Return if screen is off
1$:
HALT ; Wait for any interrupt
NOP ; HALT sometimes skips the next instruction
LD A,(.vbl_done) ; Was it a VBlank interrupt?
;; Warning: we may lose a VBlank interrupt, if it occurs now
OR A
JR Z,1$ ; No: back to sleep!
XOR A
LD (.vbl_done),A
RET
.display_off::
_display_off::
;; Check if the screen is on
LDH A,(.LCDC)
ADD A
RET NC ; Return if screen is off
1$: ; We wait for the *NEXT* VBL
LDH A,(.LY)
CP #0x92 ; Smaller than or equal to 0x91?
JR NC,1$ ; Loop until smaller than or equal to 0x91
2$:
LDH A,(.LY)
CP #0x91 ; Bigger than 0x90?
JR C,2$ ; Loop until bigger than 0x90
LDH A,(.LCDC)
AND #0b01111111
LDH (.LCDC),A ; Turn off screen
RET
;; Copy OAM data to OAM RAM
.start_refresh_OAM:
LD A,#>.OAM
LDH (.DMA),A ; Put A into DMA registers
LD A,#0x28 ; We need to wait 160 ns
1$:
DEC A
JR NZ,1$
RET
.end_refresh_OAM:
;; Serial interrupt
.serial_IO::
LD A,(__io_status) ; Get status
CP #.IO_RECEIVING
JR NZ,10$
;; Receiving data
LDH A,(.SB) ; Get data byte
LD (__io_in),A ; Store it
LD A,#.IO_IDLE
JR 11$
10$:
CP #.IO_SENDING
JR NZ,99$
;; Sending data
LDH A,(.SB) ; Get data byte
CP #.DT_RECEIVING
JR Z,11$
LD A,#.IO_ERROR
JR 12$
11$:
LD A,#.IO_IDLE
12$:
LD (__io_status),A ; Store status
XOR A
LDH (.SC),A ; Use external clock
LD A,#.DT_IDLE
LDH (.SB),A ; Reply with IDLE byte
99$:
LD A,#0x80
LDH (.SC),A ; Enable transfer with external clock
RET
_mode::
LDA HL,2(SP) ; Skip return address
LD L,(HL)
LD H,#0x00
CALL .set_mode
RET
_enable_interrupts::
EI
RET
_disable_interrupts::
DI
RET
.reset::
_reset::
LD A,(__cpu)
JP .code_start
_set_interrupts::
DI
LDA HL,2(SP) ; Skip return address
XOR A
LDH (.IF),A ; Clear pending interrupts
LD A,(HL)
LDH (.IE),A
EI ; Enable interrupts
RET
_add_VBL::
PUSH BC
LDA HL,4(SP) ; Skip return address and registers
LD C,(HL)
INC HL
LD B,(HL)
CALL .add_VBL
POP BC
RET
_add_LCD::
PUSH BC
LDA HL,4(SP) ; Skip return address and registers
LD C,(HL)
INC HL
LD B,(HL)
CALL .add_LCD
POP BC
RET
_add_TIM::
PUSH BC
LDA HL,4(SP) ; Skip return address and registers
LD C,(HL)
INC HL
LD B,(HL)
CALL .add_TIM
POP BC
RET
_add_SIO::
PUSH BC
LDA HL,4(SP) ; Skip return address and registers
LD C,(HL)
INC HL
LD B,(HL)
CALL .add_SIO
POP BC
RET
_add_JOY::
PUSH BC
LDA HL,4(SP) ; Skip return address and registers
LD C,(HL)
INC HL
LD B,(HL)
CALL .add_JOY
POP BC
RET
.area _HEAP
_malloc_heap_start::